package roborally.model;

import java.util.Random;

import roborally.exceptions.InvalidPositionException;

import be.kuleuven.cs.som.annotate.*;

/**
 * A class for dealing with surprise boxes, involving a weight.
 * When a robot uses a surprise box, one of the following events occur (selected at random):
 * the surprise box explodes and the robot is hit, the robot is teleported to a random position, 
 * or the surprise box contains another item (either a battery, a repair kit or a new surprise box) 
 * and this item is added to the list of items carried by the robot.      
 * 
 * @version 1.0
 * @author  Pieter Robberechts  (1e Bach Informatica)
 * @author	Jeroen Sanders      (1e Bach Informatica)
 *
 */
public class SurpriseBox extends Item {

	/**
	 * Initialize this new surprise box with a given weight.
	 * 
	 * @param	weight
	 * 			The weight for this new surprise box.
	 * @effect	...
	 * 			| super(weight)
	 */
	public SurpriseBox(Weight weight) {
		super(weight);
	}

	/**
	 * Return a standard weight for a repair kit (in grams).
	 */
	@Override @Basic @Raw
	public Weight getStandardWeight() {
		return new Weight(100);
	}

	/**
	 * Let's a robot use a surprise box.
	 * 
	 * @pre		...
	 * 			| robot != null
	 * @pre		...
	 * 			| robot.getBoard() != null && robot.getPosition() != null
	 * @pre 	...
	 * 			| for some Position:
	 * 			|		robot.getBoard().canHaveElementOnPosition(robot, Position)
	 * 
	 * @effect	...
	 * 			| robot.drop(this)
	 * 
	 * @post	...
	 * 			| this.isTerminated()
	 * @post	...
	 * 			| new.robot.getMaxEnergy().isIdenticalTo(robot.getMaxEnergy().subtract(new EnergyAmount(4000))) ||
	 * 			| !new.robot.getPosition().equals(robot.getPosition()) ||
	 * 			| new.robot.getPosition().equals(robot.getPosition()) ||
	 * 			| new.robot.getItems().contains(new Battery(new EnergyAmount(1000), new Weight(100))) ||
	 * 			| new.robot.getItems().contains(new RepairKit(new EnergyAmount(4000), new Weight(100))) ||
	 * 			| new.robot.getItems().contains(new SurpriseBox(new Weight(100)))
	 */
	@Override
	public void use(Robot robot) {
		int rnd = (new Random()).nextInt(5);
		
		switch(rnd) {
			case 0: // SurpriseBox explodes
				robot.damageRobot(new EnergyAmount(4000));
				break;
			case 1: // Random teleport
				Random randomGenerator = new Random();
				Position rndPosition;
				do{
					long rndX = (long)(randomGenerator.nextDouble()*(robot.getBoard().getWidth()+1));
					long rndY = (long)(randomGenerator.nextDouble()*(robot.getBoard().getHeight()+1));
					rndPosition = new Position(rndX,rndY);
				}
				while(!robot.getBoard().canHaveElementOnPosition(robot, rndPosition));
				robot.getBoard().putElement(rndPosition, robot);
				break;
			case 2: // New battery
				Item battery = new Battery(new EnergyAmount(1000), new Weight(100));
				robot.getBoard().putElement(robot.getPosition(), battery);
				robot.pickUp(battery);
				break;
			case 3: // New repairkit
				Item repairKit = new RepairKit(new EnergyAmount(4000), new Weight(100));
				robot.getBoard().putElement(robot.getPosition(), repairKit);
				robot.pickUp(repairKit);
				break;
			case 4: // New SurpriseBox
				Item surpriseBox = new SurpriseBox(new Weight(100));
				robot.getBoard().putElement(robot.getPosition(), surpriseBox);
				robot.pickUp(surpriseBox);
				break;
		}
		
		robot.drop(this);
		terminate();
	}

	/**
	 * Executes the effects of a laser on the surprise box.
	 * 
	 * @post	...
	 * 			| isTerminated() == true
	 * 
	 * @effect	...
	 * 			| terminate() &&
	 * 			| for each Orientation:
	 * 			|		explode(Orientation)
	 */
	@Override
	public void damageByLaser() {
		Board board = getBoard();
		Position position = getPosition();
		terminate();
		explode(board,position,Orientation.UP);
		explode(board,position,Orientation.RIGHT);
		explode(board,position,Orientation.DOWN);
		explode(board,position,Orientation.LEFT);
	}
	
	/**
	 * Does explosion damage to the neighbour position.
	 * 
	 * @param	orientation
	 * 			The orientation used for selecting which neighbour.
	 * 
	 * @param	board
	 * 			The board of the terminated surprise box.
	 * 
	 * @param	position
	 * 			The position of the terminated suprise box.
	 * 
	 * @effect	...
	 * 			| getBoard().getElements(getPosition().getNeighbour(orientation))
	 * 			| .iterator().next().damageByLaser()
	 */
	@Model
	private void explode(Board board, Position position ,Orientation orientation) {
		try {
		Iterable<Element> elements = board.getElements(position.getNeighbour(orientation));
		if(elements.iterator().hasNext()) {
			elements.iterator().next().damageByLaser();
		}
		} catch(InvalidPositionException e) {}
	}
	
    /**
     * Return a textual representation of a surprise box.
     * 
     * @return      ...
     *                      | if( getPosition() != null )
     *                      |       then result == "Surprise box [Weight: " + getWeight() + ", Position: " + getPosition().toString() + "]";
     *                      | else
     *                      |       then result == "Surprise box [Weight " + getWeight() + " ]";
     * @return      ...
     *                      | if isTerminated()
     *                      |       then result == "This surprise box is terminated."
     */
    @Override
    public String toString(){
            if(isTerminated())
                    return "This surprise box is terminated.";
            if(getPosition() != null)
                    return "Surprise box [Weight: " + getWeight() + ", Position: " + getPosition().toString() + "]";
            else
                    return "Surprise box [Weight " + getWeight() + " ]";

    }
	
}
